package com.cantor.provider.regsitry;

import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.cantor.common.exception.InvalidServiceBoatException;
import com.cantor.common.exception.NoSuchServiceImplException;
import com.cantor.common.exception.RepetitiveRegisterException;
import com.cantor.common.util.CantorUtil;
import com.cantor.core.center.RegistrationCenter;
import com.cantor.core.config.CantorAppConfig;
import com.cantor.core.config.CantorAppConfigProperties;
import com.cantor.provider.pojo.ServiceBoat;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务注册者抽象实现类
 */
@Slf4j
public abstract class AbstractServiceRegistrant implements ServiceRegistrant {

    // 维护一个Map<服务接口名:String, 服务载体:ServiceBoat>
    // 同一个服务可能有多个版本,用":"分隔, 例如: com.service.HelloService:v1.0
    protected Map<String, ServiceBoat> registeredServicesMap;

    public AbstractServiceRegistrant() {
        registeredServicesMap = new ConcurrentHashMap<>();
    }

    @Override
    public void register(ServiceBoat boat) {
        // 检查boat里面必要的数据是否为null
        if (ObjectUtil.isNull(boat.getServiceInterface())) {
            throw new InvalidServiceBoatException();
        }
        if (ObjectUtil.isNull(boat.getServiceImpl())) {
            throw new InvalidServiceBoatException();
        }
        if (ObjectUtil.isNull(boat.getVersion())) {
            throw new InvalidServiceBoatException("ServiceBoat中version字段为null, 如果不想填,请设置为空字符串");
        }
        // 往后面拼接服务的版本信息, 同一个服务的不同版本可能被注册进Map, 如果没指定version, 可能字符串拼接为null
        String serviceInterfaceName = boat.getServiceInterface().getName();
        if (StrUtil.isNotEmpty(boat.getVersion())) {
            serviceInterfaceName += ":" + boat.getVersion();
        }
        if (registeredServicesMap.containsKey(serviceInterfaceName)) {
            throw new RepetitiveRegisterException();
        }
        registeredServicesMap.put(serviceInterfaceName, boat);
    }

    @Override
    public <T> ServiceBoat<T> getBoat(String serviceInterfaceName) throws NoSuchServiceImplException {
        if (!registeredServicesMap.containsKey(serviceInterfaceName)) {
            throw new NoSuchServiceImplException();
        }
        return registeredServicesMap.get(serviceInterfaceName);
    }

    @Override
    public Map<String, ServiceBoat> allServiceBoats() {
        return registeredServicesMap;
    }

    @Override
    public void registerAllServicesToCenter(CantorAppConfig appConfig,RegistrationCenter center) {
        registeredServicesMap.forEach((name, boat) -> {
            /**
             * 在指定的com.service.xxx下注册自己的地址和端口
             * 节点信息样子长这样:
             * /192.168.1.9:8868?version=&loadBalance=&timeout=&weight=&retries=&mock=
             */
            CantorAppConfigProperties appProperties = appConfig.getProperties();
            CantorAppConfigProperties.ProviderConfig providerConfig = appProperties.getProviderConfig();
            // 构建准备注册到远程注册中心的字符串, 顺便初始化ServiceBoat中没有设的值
            // init ServiceBoat
            if (StrUtil.isEmpty(boat.getVersion())) {
                boat.setVersion(providerConfig.getVersion());
            }
            if (0 == boat.getTimeout()) {
                boat.setTimeout(providerConfig.getTimeout());
            }
            if (0 == boat.getWeight()) {
                boat.setWeight(providerConfig.getWeight());
            }
            if (StrUtil.isEmpty(boat.getMock())) {
                boat.setMock(providerConfig.getMock());
            }
            // build url
            String nodePath = CantorUtil.buildNodePath(UrlBuilder.create()
                    // .setHost(InetAddress.getLocalHost().getHostAddress())
                    .setHost(CantorUtil.getExactLocalHost())
                    .setPort(appProperties.getPort())
                    .addQuery("version", boat.getVersion())
                    .addQuery("timeout", StrUtil.toString(boat.getTimeout()))
                    .addQuery("weight", StrUtil.toString(boat.getWeight()))
                    .addQuery("mock", boat.getMock())
                    .addQuery("interface", boat.getServiceInterface().getName())
                    .addQuery("methods", CantorUtil.getMethodNamesStr(boat.getServiceInterface())));
            // 注册
            // 先删再建
            if(center.exists(name,nodePath)){
                center.deleteNode(name,nodePath);
            }
            // 携带data为0表示被调用了0次
            if (center.createMultiNodeEWithData("0",name, nodePath)) {
                log.info("----------------- {} 已经被注册到远程中心", nodePath);
            } else {
                registeredServicesMap.remove(name);
                log.error("################# {} 被注册到远程中心时失败, 已从map中移除", nodePath);
            }
        });
    }

}
